﻿/*
 * This file is part of CommunitySettlers.
 *
 * CommunitySettlers is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * CommunitySettlers is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with CommunitySettlers.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Author: Christoph Husse
 * 
 * Also checkout our homepage: http://communitysettlers.codeplex.com/
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Markup;
using System.Windows.Threading;
using System.Diagnostics;

namespace SettlersEngine
{
    internal enum PrecisionQuota : int
    {
        To100ms = 0,
        To500ms = 1,
        To1000ms = 2,
        Lowest = 3,
    }

    internal delegate void DPrecisionTask(Int64 inAbsoluteMillis, Object inCallback);

    internal static class WPFPrecisionSchedule
    {
        public static Int64 ElapsedMillis { get { return m_Watch.ElapsedMilliseconds; } }
        public static TimeSpan TimerInterval { get { return m_Timer.Interval; } set { m_Timer.Interval = value; } }

        private class PrecisionTask
        {
            public DPrecisionTask Func;
            public Int64 AbsMillis;
            public Object Callback;
        }

        private static DispatcherTimer m_Timer = new DispatcherTimer();
        private static Stopwatch m_Watch = new Stopwatch();
        private static Int64 m_Last500Step = 0, m_Last1000Step = 0, m_LastHigherStep = 0;
        // TODO: exchange with a fixed list (for performance)
        private static LinkedList<PrecisionTask>[] m_Tasks = new LinkedList<PrecisionTask>[]
        {
            new LinkedList<PrecisionTask>(),
            new LinkedList<PrecisionTask>(),
            new LinkedList<PrecisionTask>(),
            new LinkedList<PrecisionTask>(),
        };

        static WPFPrecisionSchedule()
        {
            // setup animation timers
            m_Watch.Start();
            TimerInterval = TimeSpan.FromMilliseconds(30);
            m_Timer.Tick += new EventHandler(m_Timer_Tick);
            m_Timer.Start();
        }

        static void m_Timer_Tick(object sender, EventArgs e)
        {
            Int64 elapsed = ElapsedMillis;

            LinkedList<PrecisionTask> tasks = m_Tasks[(int)PrecisionQuota.To100ms];

            ProcessTasks(elapsed, tasks);

            tasks = m_Tasks[(int)PrecisionQuota.To500ms];
            if (ElapsedMillis - m_Last500Step > 100)
            {
                ProcessTasks(elapsed, tasks);
                m_Last500Step = elapsed;
            }

            tasks = m_Tasks[(int)PrecisionQuota.To1000ms];
            if (ElapsedMillis - m_Last1000Step > 500)
            {
                ProcessTasks(elapsed, tasks);
                m_Last1000Step = elapsed;
            }

            tasks = m_Tasks[(int)PrecisionQuota.Lowest];
            if (ElapsedMillis - m_LastHigherStep > 1000)
            {
                ProcessTasks(elapsed, tasks);
                m_LastHigherStep = elapsed;
            }
        }

        private static void ProcessTasks(Int64 elapsed, LinkedList<PrecisionTask> tasks)
        {   
            LinkedListNode<PrecisionTask> list = tasks.First;
            
            while(list != null)
            {
                LinkedListNode<PrecisionTask> next = list.Next;

                if (list.Value.AbsMillis < elapsed)
                {
                    tasks.Remove(list);

                    list.Value.Func(list.Value.AbsMillis, list.Value.Callback);
                }

                list = next;
            }
        }

        public static void QueueTask(DPrecisionTask inTask, Int64 inRelativeMillis)
        {
            QueueTask(inTask, inRelativeMillis, null);
        }

        public static void QueueTask(DPrecisionTask inTask, Int64 inRelativeMillis, Object inCallback)
        {
            PrecisionQuota quota;

            if (inRelativeMillis < 100)
                quota = PrecisionQuota.To100ms;
            else if (inRelativeMillis < 500)
                quota = PrecisionQuota.To500ms;
            else if (inRelativeMillis < 1000)
                quota = PrecisionQuota.To1000ms;
            else
                quota = PrecisionQuota.Lowest;

            QueueTask(inTask, inRelativeMillis, quota, inCallback);
        }

        public static void QueueTask(DPrecisionTask inTask, Int64 inRelativeMillis, PrecisionQuota inQuota)
        {
            QueueTask(inTask, inRelativeMillis, inQuota, null);
        }

        public static void QueueTask(DPrecisionTask inTask, Int64 inRelativeMillis, PrecisionQuota inQuota, Object inCallback)
        {
            m_Tasks[(int)inQuota].AddLast(new PrecisionTask() { 
                AbsMillis = ElapsedMillis + inRelativeMillis,
                Func = inTask,
                Callback = inCallback,
            });
        }
    }
}
